To Do:
- dont’ force 2 axes on simulated data? (sometimes models fail though. Would need to use a “safe” version of
opls())
Overview
After discussion with Elizabeth we decided to create the following combination of datasets:
- Without signal (no covariance) and without discriminating variables. We would predict poor PCA and no separation by PLS-DA
- With signal (covariance) and without discriminating variables. We would expect good PCA (high % var explained), but no separation in PCA or PLS-DA
- Without signal (covariance) and with discriminating variables. We would expect poor PCA, but significant PLS-DA
- With signal and discriminating variables. We would expect good PCA, but little separation in PCA space if only a few discriminating variables, but significant PLS-DA.
I am consider making a Shiny app that allows you to tweak the parameters that generate the data and plots PCA and PLS-DA side-by-side.
Permutation testing: What about using each of these 4 parameter combinations to create ~100 randomly generated datasets. Then with each, I could do PCA followed by t-tests on the PC axes vs. PLS-DA to demonstrate which is better at detecting real separation in the data. Maybe the cherry-picked figure is enough, but this would only be a few sentences in the manuscript and would be valuable I think.
To-Do: Add MANNOVA or PERMANOVA to this
Setup
Load Packages
# Required Packages
library(MASS)
library(tidyverse)
library(ropls)
library(chemhelper)
#chemhelper contains the sim_multvar() function I wrote as well as custom functions for interfacing with ropls package in a friendlier way.
#Install with devtools::install_github("Aariq/chemhelper")
library(cowplot) #for making and saving prettier plots
library(broom) #for tidy(), which turns model output into dataframes
library(vegan) #for PERMANOVA
library(iheatmapr) #for correlation heatmaps
Create Custom Plotting Functions
# Functions for plotting
library(latex2exp)
pca_plot <- function(pca.opls, group_var){
plotdata <- chemhelper::get_plotdata(pca.opls)
ggplot(plotdata$plot_data, aes(x = p1, y = p2, color = group_var)) +
geom_point() +
stat_ellipse() +
labs(x = paste0("PC1 (", plotdata$var_explained$R2X[1] * 100, "%)"),
y = paste0("PC2 (", plotdata$var_explained$R2X[2] * 100, "%)")) +
scale_colour_discrete("Group Membership") +
theme_bw() +
ggtitle("PCA") +
labs(caption = TeX(
paste0(nrow(plotdata$var_explained), " principal components;",
"$R^2(cumulative) = ", max(plotdata$var_explained$`R2X(cum)`, "$"))))
}
plsda_plot <- function(plsda.opls){
plotdata <- chemhelper::get_plotdata(plsda.opls)
ggplot(plotdata$plot_data, aes(x = p1, y = p2, color = y1)) +
geom_point() +
stat_ellipse() +
labs(x = paste0("P1 (", plotdata$axis_stats$R2X[1] * 100, "%)"),
y = paste0("P2 (", plotdata$axis_stats$R2X[2] * 100, "%)")) +
scale_color_discrete("Group Membership") +
theme_bw() +
ggtitle("PLS-DA") +
labs(caption = TeX(
paste0("$R^{2}_{Y} = ", plotdata$model_stats$`R2Y(cum)`, "$; ",
"$Q^{2} = ", plotdata$model_stats$`Q2(cum)`, "$; ",
"$p_{Q^{2}} = ", plotdata$model_stats$pQ2, "$")))
}
oplsda_plot <- function(oplsda.opls){
plotdata <- chemhelper::get_plotdata(oplsda.opls)
ggplot(plotdata$plot_data, aes(x = p1, y = o1, color = y1)) +
geom_point() +
stat_ellipse() +
labs(x = paste0("Pred (", plotdata$axis_stats$R2X[1] * 100, "%)"),
y = paste0("Ortho (", plotdata$axis_stats$R2X[2] * 100, "%)")) +
scale_color_discrete("Group Membership") +
theme_bw() +
ggtitle("OPLS-DA") +
labs(caption = TeX(
paste0("$R^{2}_{Y}=", plotdata$model_stats$`R2Y(cum)`, "$; ",
"$Q^{2}=", plotdata$model_stats$`Q2(cum)`, "$; ",
"$p_{Q^{2}}=", plotdata$model_stats$pQ2, "$")))
}
Create Custom Summary Table Function
my_table <- function(data, pca, plsda){
VIPs <- get_VIP(plsda)
loadings_pls <- getLoadingMN(plsda) %>%
as.data.frame() %>%
rownames_to_column(var = "Variable") %>%
rename(p1_loading = p1, p2_loading = p2)
loadings_pca <- getLoadingMN(pca) %>%
as.data.frame() %>%
rownames_to_column(var = "Variable") %>%
select(Variable, PC1_loading = p1, PC2_loading = p2)
t_tests <- data %>%
select(-group) %>%
map_df(~t.test(.~data$group)$p.value) %>%
gather(key = Variable, value = t_test_p.value)
join1 <- full_join(VIPs, loadings_pls)
join2 <- full_join(join1, loadings_pca)
join3 <- full_join(join2, t_tests)
return(join3)
}
Create safe version of opls()
Occasionally I was getting ‘matrix is singular’ type errors from opls(), which I think is just a chance occurrance with the random data. This “safe” version will always return NULL when there is any kind of error.
safe.opls <- possibly(opls, otherwise = NULL)
Set global options (needle in haystack scenario)
I’ll set a few global options here to make it easier to play around with the simulation.
These options basically create a situation where when there are real differences between groups, it’s only due to a small percentage of variables. Other variables can have mild covariation or none at all.
N = 30 #total number of observations
P = 40 #total number of variables
signal.prop = 0.625 #when there are correlated variables, what proportion?
cov_corvar = 0.8 #covariance for signal variables
disc.prop = 0.125 #when there are discriminating variables, what proportion?
diff_discr = 1.2 #mean difference between groups for discriminating variables
nperm = 50 #how many perumutations
seed = 100 #seed
#calculated values
(p_corvar = round(P*signal.prop))
[1] 25
(p_discr = round(P*disc.prop))
[1] 5
1. No Covariance, No Discrimination
Generate data
sim.data1 <- sim_multvar(p_uncorvar = P,
p_corvar = 0,
p_discr = 0,
cov_corvar = 0,
diff_discr = 0,
N = N,
seed = seed)
sim.data1 %>% select(-group) %>% as.matrix() %>% cor() %>% iheatmap(row_labels = TRUE, col_labels = TRUE)
PCA & PLS-DA
Run PCA
sim.pca1 <- opls(select(sim.data1, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
R2X(cum) pre ort
Total 0.555 7 0
Run PLS-DA
sim.plsda1 <- try(opls(select(sim.data1, -group), sim.data1$group,
plotL = FALSE))
Error : No model was built because the first predictive component was already not significant;
Select a number of predictive components of 1 if you want the algorithm to compute a model despite this.
This fails, but I’ll force two axes for the sake of plotting.
## Must force axes
sim.plsda1 <- opls(select(sim.data1, -group), sim.data1$group,
predI = 2,
permI = 200,
plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.148 0.751 -0.765 0.263 2 0 0.64 0.36
Plots:
p1 <- plot_grid(pca_plot(sim.pca1, sim.data1$group) +
theme(legend.position = "none") +
ggtitle("PCA", subtitle = "No covariance, no discriminating variables"),
plsda_plot(sim.plsda1) +
theme(legend.position = "none") +
ggtitle("PLS-DA", subtitle = "No covariance, no discriminating variables"))
p1

Get PC axis loadings and VIP scores.
Note: It’s probably not very responsible to look at VIP scores from a PLS-DA model that is not significant
my_table(sim.data1, sim.pca1, sim.plsda1) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"
Permutation testing
First, make a bunch of datasets with the same parameters
sim.data1.list <- map(1:nperm,
~sim_multvar(p_uncorvar = P,
p_corvar = 0,
p_discr = 0,
cov_corvar = 0,
diff_discr = 0,
N = N,
seed = NA))
names(sim.data1.list) <- 1:nperm
Now, do PCA on all of them and get scores
Now do t-tests on all of them along PCs 1 and 2 and get p-values
#The 'group' variable is the same in all datasets.
grouping <- sim.data1.list[[1]]$group
p1.testresults <- scores.data1.list %>%
#maps t.test() function to all dataframes and converts output into dataframe with tidy()
map_dfr(~t.test(.$p1 ~ grouping) %>% tidy(), .id = "dataset") %>%
#selects just columns of interest
select(dataset,
PC1.effect.size = "estimate",
PC1.t = "statistic",
PC1.p.value = "p.value")
p2.testresults <- scores.data1.list %>%
map_dfr(~t.test(.$p2 ~ grouping) %>% tidy(), .id = "dataset") %>%
select(dataset,
PC2.effect.size = "estimate",
PC2.t = "statistic",
PC2.p.value = "p.value")
data1.PCAresults <- bind_cols(p1.testresults, p2.testresults)
And do PERMANOVA on all of them
data1.permanova <- map_dbl(sim.data1.list,
~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])
Now do PLS-DA on all of them and get p-values (this will take a long time)
data1.PLSresults <- pls.data1.list %>%
map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data1.comparison <- full_join(data1.PCAresults, data1.PLSresults) %>% add_column("permanova" = data1.permanova)
p1.perm <- ggplot(data1.comparison) +
geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
geom_density(aes(permanova), fill = "green", alpha = 0.33) +
labs(x = "p value") +
geom_vline(xintercept = 0.05, linetype = 5) +
ggtitle("1. -Covariance, -Discriminating variables")
p1.perm

2. Yes Covariance, No Discrimination
PCA & PLS-DA
Run PCA
sim.pca2 <- opls(select(sim.data2, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
R2X(cum) pre ort
Total 0.523 3 0
Run PLS-DA
sim.plsda2 <- try(opls(select(sim.data2, -group), sim.data2$group,
plotL = FALSE))
Error : No model was built because the first predictive component was already not significant;
Select a number of predictive components of 1 if you want the algorithm to compute a model despite this.
This fails, but I’ll force two axes for the sake of plotting.
## Must force axes
sim.plsda2 <- opls(select(sim.data2, -group), sim.data2$group,
predI = 2,
permI = 200, plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.247 0.317 -3.43 0.436 2 0 1 1
Plots:
p2 <- plot_grid(pca_plot(sim.pca2, sim.data2$group) +
theme(legend.position = "none") +
ggtitle("PCA", subtitle = "Yes covariance, no discriminating variables"),
plsda_plot(sim.plsda2) +
theme(legend.position = "none") +
ggtitle("PLS-DA", subtitle = "Yes covariance, no discriminating variables"))
p2

PC1 slightly better, but overall R^2 the same. PLS-DA still not significant
Get PC axis loadings and VIP scores.
Note: It’s probably not responsible to look at VIP scores from a PLS-DA model that is not significant
my_table(sim.data2, sim.pca2, sim.plsda2) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"
Permutation testing
First, make a bunch of datasets with the same parameters
sim.data2.list <- map(1:nperm,
~sim_multvar(p_uncorvar = P - p_corvar,
p_corvar = p_corvar,
p_discr = 0,
cov_corvar = cov_corvar,
diff_discr = 0,
N = N,
seed = NA))
names(sim.data2.list) <- 1:nperm
Now, do PCA on all of them and get loadings.
Now do t-tests on all of them along PCs 1 and 2 and get p-values
#The 'group' variable is the same in all datasets.
grouping <- sim.data2.list[[1]]$group
p1.testresults <- scores.data2.list %>%
#maps t.test() function to all dataframes and converts output into dataframe with tidy()
map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>%
#selects just columns of interest
select(dataset,
PC1.effect.size = "estimate",
PC1.t = "statistic",
PC1.p.value = "p.value")
p2.testresults <- scores.data2.list %>%
map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
select(dataset,
PC2.effect.size = "estimate",
PC2.t = "statistic",
PC2.p.value = "p.value")
Error in model.frame.default(formula = .$p2 ~ grouping) :
invalid type (NULL) for variable '.$p2'
And do PERMANOVA on all of them
data2.permanova <- map_dbl(sim.data2.list,
~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])
Now do PLS-DA on all of them and get p-values (this will take a long time)
data2.PLSresults <- pls.data2.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data2.PLSresults
data2.comparison <- full_join(data2.PCAresults, data2.PLSresults) %>%
add_column("permanova" = data2.permanova)
p2.perm <- ggplot(data2.comparison) +
geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
geom_density(aes(permanova), fill = "green", alpha = 0.33) +
labs(x = "p value") +
geom_vline(xintercept = 0.05, linetype = 5) +
ggtitle("2. + Covariance, - Discriminating variables")
p2.perm

3. No Covariance, Yes Discrimination
PCA & PLS-DA
Run PCA
sim.pca3 <- opls(select(sim.data3, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
R2X(cum) pre ort
Total 0.534 6 0
Run PLS-DA
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.133 0.614 0.331 0.322 1 0 0.265 0.015
Single component model only, so force two axes for the sake of plotting:
sim.plsda3 <- opls(select(sim.data3, -group), sim.data3$group,
permI = 200,
predI = 2,
plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.195 0.794 -0.0334 0.239 2 0 0.23 0.06
Plots:
p3 <- plot_grid(pca_plot(sim.pca3, sim.data3$group) +
theme(legend.position = "none") +
ggtitle("PCA", subtitle = "No covariance, yes discriminating variables"),
plsda_plot(sim.plsda3) +
theme(legend.position = "none") +
ggtitle("PLS-DA", subtitle = "No covariance, yes discriminating variables"))
p3

PLS-DA is still not great. Single component model is signifcant. Forced 2 axes for PLS-DA plot. It might be the case that some co-variance between discriminating variables is required for PLS-DA to pull them out. If they are completely orthogonal, how can it draw the “regression line” through 5-dimensional space?
Get PC axis loadings and VIP scores.
my_table(sim.data3, sim.pca3, sim.plsda3) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"
Permutation testing
First, make a bunch of datasets with the same parameters
sim.data3.list <- map(1:nperm,
~sim_multvar(p_uncorvar = P - p_discr,
p_corvar = 0,
p_discr = p_discr,
cov_corvar = 0,
diff_discr = diff_discr,
N = N,
seed = NA))
Now, do PCA on all of them and get loadings.
Now do t-tests on all of them along PCs 1 and 2 and get p-values
#The 'group' variable is the same in all datasets.
grouping <- sim.data3.list[[1]]$group
p1.testresults <- scores.data3.list %>%
#maps t.test() function to all dataframes and converts output into dataframe with tidy()
map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>%
#selects just columns of interest
select(dataset,
PC1.effect.size = "estimate",
PC1.t = "statistic",
PC1.p.value = "p.value")
p2.testresults <- scores.data3.list %>%
map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
select(dataset,
PC2.effect.size = "estimate",
PC2.t = "statistic",
PC2.p.value = "p.value")
data3.PCAresults <- bind_cols(p1.testresults, p2.testresults)
And do PERMANOVA on all of them
data3.permanova <- map_dbl(sim.data3.list,
~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])
Now do PLS-DA on all of them and get p-values (this will take a long time)
data3.PLSresults <- pls.data3.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data3.PLSresults
data3.comparison <- full_join(data3.PCAresults, data3.PLSresults) %>% add_column("permanova" = data3.permanova)
p3.perm <- ggplot(data3.comparison) +
geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
geom_density(aes(permanova), fill = "green", alpha = 0.33) +
labs(x = "p value") +
geom_vline(xintercept = 0.05, linetype = 5) +
ggtitle("3. - Covariance, + Discriminating variables")
p3.perm

4. Yes Covariance, Yes Discrimination
PCA & PLS-DA
Run PCA
sim.pca4 <- opls(select(sim.data4, -group), plotL = FALSE)
PCA
30 samples x 40 variables
standard scaling of predictors
R2X(cum) pre ort
Total 0.565 3 0
Run PLS-DA
sim.plsda4 <- opls(select(sim.data4, -group), sim.data4$group,
permI = 200,
plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.115 0.518 0.159 0.359 1 0 0.015 0.05
This produces a 1-component model. I’ll force two axes for the sake of plotting:
sim.plsda4 <- opls(select(sim.data4, -group), sim.data4$group,
permI = 200,
predI = 2,
plotL = FALSE)
PLS-DA
30 samples x 40 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.354 0.604 -0.143 0.332 2 0 0.085 0.335
Plots:
p4 <- plot_grid(pca_plot(sim.pca4, sim.data4$group) +
theme(legend.position = "none") +
ggtitle("PCA", subtitle = "Yes covariance, Yes discriminating variables"),
plsda_plot(sim.plsda4) +
theme(legend.position = "none") +
ggtitle("PLS-DA", subtitle = "Yes covariance, Yes discriminating variables"))
p4

Get PC axis loadings and VIP scores.
my_table(sim.data4, sim.pca4, sim.plsda4) %>% arrange(desc(VIP))
Joining, by = "Variable"
Joining, by = "Variable"
Joining, by = "Variable"
Permutation testing
First, make a bunch of datasets with the same parameters
sim.data4.list <- map(1:nperm,
~sim_multvar(p_uncorvar = P - p_corvar - p_discr,
p_corvar = p_corvar,
p_discr = p_discr,
cov_corvar = cov_corvar,
diff_discr = diff_discr,
cov_discr = 0.2,
N = N,
seed = NA)) %>%
set_names(1:nperm)
Now, do PCA on all of them and get loadings.
Now do t-tests on all of them along PCs 1 and 2 and get p-values
#The 'group' variable is the same in all datasets.
grouping <- sim.data4.list[[1]]$group
p1.testresults <- scores.data4.list %>%
#maps t.test() function to all dataframes and converts output into dataframe with tidy()
map_dfr(~t.test(.$p1~grouping) %>% tidy(), .id = "dataset") %>%
#selects just columns of interest
select(dataset,
PC1.effect.size = "estimate",
PC1.t = "statistic",
PC1.p.value = "p.value")
# p2.testresults <- scores.data4.list %>%
# map_dfr(~t.test(.$p2~grouping) %>% tidy(), .id = "dataset") %>%
# select(dataset,
# PC2.effect.size = "estimate",
# PC2.t = "statistic",
# PC2.p.value = "p.value")
# data4.PCAresults <- bind_cols(p1.testresults, p2.testresults)
data4.PCAresults <- p1.testresults
And do PERMANOVA on all of them
data4.permanova <- map_dbl(sim.data4.list,
~ adonis(select(., -group) ~ grouping, method = "eu")$aov.tab$`Pr(>F)`[1])
Now do PLS-DA on all of them and get p-values (this will take a long time)
data4.PLSresults <- pls.data4.list %>% map_dfr(~get_plotdata(.)$model_stats, .id = "dataset")
data4.PLSresults
data4.comparison <- full_join(data4.PCAresults, data4.PLSresults) %>% add_column("permanova" = data4.permanova)
p4.perm <- ggplot(data4.comparison) +
geom_density(aes(PC1.p.value), fill = "blue", alpha = 0.33) +
geom_density(aes(pQ2), fill = "red", alpha = 0.33) +
geom_density(aes(permanova), fill = "green", alpha = 0.33) +
labs(x = "p value") +
geom_vline(xintercept = 0.05, linetype = 5) +
ggtitle("4. + Covariance, + Discriminating variables")
p4.perm

Plot example datasets

Plot permutation testing results

Conclusions
None of the methods give more false positives than they should—that is, no differences are found when there are no discriminating variables.
PERMANOVA seems to perform the best in these simulations, followed by PLS-DA. PCA is shit at finding separation when discriminating variables are a small portion of the total variables measured (needle in a haystack scenario).
When covariance is added to the “haystack”, or to both the “haystack” and the “needle”, the performance of PERMANOVA drops, while the performance of PLS-DA remains about the same. I should play around with this in a separate notebook.
Trying alternate method of making discriminating variables
I can’t even figure out how to make PLS-DA find a difference.
sim.data5 <- sim.vcov(p_noise = 0,
p_signal = 15,
p_disc = 15,
cov_signal = 0.8,
cov_disc = 0.6,
diff_disc = 0,
var = 0.8,
N = 20,
seed = NA)
sim.data5 <- sim.data5 %>%
rename(Y = disc_1) %>%
arrange(Y) %>%
mutate(group = c(rep("a", nrow(.)/2), rep("b", nrow(.)/2)))
pca5 <- opls(select(sim.data5, -Y, -group), predI = 2)
PCA
20 samples x 29 variables
standard scaling of predictors
R2X(cum) pre ort
Total 0.882 2 0

pca_plot(pca5, sim.data5$group)

plsda5 <- opls(select(sim.data5, -group, -Y), sim.data5$group, permI = 200, predI = 2)
PLS-DA
20 samples x 29 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.882 0.507 0.327 0.381 2 0 0.08 0.025

get_VIP(plsda5) %>% arrange(desc(VIP))
pls5 <- opls(select(sim.data5, -group, -Y), sim.data5$Y, permI = 200, predI = 2)
PLS
20 samples x 29 variables and 1 response
standard scaling of predictors and response(s)
R2X(cum) R2Y(cum) Q2(cum) RMSEE pre ort pR2Y pQ2
Total 0.88 0.64 0.471 0.498 2 0 0.02 0.005

Huh, that doesn’t work
plotdata <- sim.data5 %>%
gather(-group, -Y, key = variable, value = data) %>%
mutate(vartype = case_when(str_detect(variable, "disc") ~ "discriminating",
str_detect(variable, "noise") ~ "no covariance",
str_detect(variable, "signal") ~ "covarying"))
ggplot(plotdata, aes(x = Y, y = data, color = vartype)) +
geom_point() +
geom_smooth(method ="lm", se = FALSE)

Ah ha! Is it because the discriminating data actually does NOT co-vary? No, I upped the covariance and it doesn’t help.
Permanova
permdata5 <- sim.data5 %>%
# select(-Y) %>%
mutate_if(is.double, scale)
adonis(select(permdata5, -group, -Y)~permdata5$group*permdata5$Y, method = "eu")
Call:
adonis(formula = select(permdata5, -group, -Y) ~ permdata5$group * permdata5$Y, method = "eu")
Permutation: free
Number of permutations: 999
Terms added sequentially (first to last)
Df SumsOfSqs MeanSqs F.Model R2 Pr(>F)
permdata5$group 1 93.01 93.015 3.8219 0.16881 0.027 *
permdata5$Y 1 54.00 53.997 2.2187 0.09800 0.104
permdata5$group:permdata5$Y 1 14.59 14.587 0.5994 0.02647 0.546
Residuals 16 389.40 24.338 0.70672
Total 19 551.00 1.00000
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
LS0tCnRpdGxlOiAiU2ltdWxhdGVkIERhdGEgQW5hbHl6ZWQgd2l0aCBQQ0EgYW5kIFBMUy1EQSIKYXV0aG9yOiBFcmljIFIgU2NvdHQKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCiMgVG8gRG86Ci0gZG9udCcgZm9yY2UgMiBheGVzIG9uIHNpbXVsYXRlZCBkYXRhPyAoc29tZXRpbWVzIG1vZGVscyBmYWlsIHRob3VnaC4gIFdvdWxkIG5lZWQgdG8gdXNlIGEgInNhZmUiIHZlcnNpb24gb2YgYG9wbHMoKWApCgoKIyBPdmVydmlldwpBZnRlciBkaXNjdXNzaW9uIHdpdGggRWxpemFiZXRoIHdlIGRlY2lkZWQgdG8gY3JlYXRlIHRoZSBmb2xsb3dpbmcgY29tYmluYXRpb24gb2YgZGF0YXNldHM6CgoxLiBXaXRob3V0IHNpZ25hbCAobm8gY292YXJpYW5jZSkgYW5kIHdpdGhvdXQgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLiAgV2Ugd291bGQgcHJlZGljdCBwb29yIFBDQSBhbmQgbm8gc2VwYXJhdGlvbiBieSBQTFMtREEKMi4gV2l0aCBzaWduYWwgKGNvdmFyaWFuY2UpIGFuZCB3aXRob3V0IGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcy4gIFdlIHdvdWxkIGV4cGVjdCBnb29kIFBDQSAoaGlnaCAlIHZhciBleHBsYWluZWQpLCBidXQgbm8gc2VwYXJhdGlvbiBpbiBQQ0Egb3IgUExTLURBCjMuIFdpdGhvdXQgc2lnbmFsIChjb3ZhcmlhbmNlKSBhbmQgKip3aXRoKiogZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLiBXZSB3b3VsZCBleHBlY3QgcG9vciBQQ0EsIGJ1dCBzaWduaWZpY2FudCBQTFMtREEKNC4gV2l0aCBzaWduYWwgKiphbmQqKiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMuICBXZSB3b3VsZCBleHBlY3QgZ29vZCBQQ0EsIGJ1dCBsaXR0bGUgc2VwYXJhdGlvbiBpbiBQQ0Egc3BhY2UgaWYgb25seSBhIGZldyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMsIGJ1dCBzaWduaWZpY2FudCBQTFMtREEuCgpJIGFtIGNvbnNpZGVyIG1ha2luZyBhIFNoaW55IGFwcCB0aGF0IGFsbG93cyB5b3UgdG8gdHdlYWsgdGhlIHBhcmFtZXRlcnMgdGhhdCBnZW5lcmF0ZSB0aGUgZGF0YSBhbmQgcGxvdHMgUENBIGFuZCBQTFMtREEgc2lkZS1ieS1zaWRlLgoKKlBlcm11dGF0aW9uIHRlc3Rpbmc6KgpXaGF0IGFib3V0IHVzaW5nIGVhY2ggb2YgdGhlc2UgNCBwYXJhbWV0ZXIgY29tYmluYXRpb25zIHRvIGNyZWF0ZSB+MTAwIHJhbmRvbWx5IGdlbmVyYXRlZCBkYXRhc2V0cy4gIFRoZW4gd2l0aCBlYWNoLCBJIGNvdWxkIGRvIFBDQSBmb2xsb3dlZCBieSB0LXRlc3RzIG9uIHRoZSBQQyBheGVzIHZzLiBQTFMtREEgdG8gZGVtb25zdHJhdGUgd2hpY2ggaXMgYmV0dGVyIGF0IGRldGVjdGluZyByZWFsIHNlcGFyYXRpb24gaW4gdGhlIGRhdGEuICBNYXliZSB0aGUgY2hlcnJ5LXBpY2tlZCBmaWd1cmUgaXMgZW5vdWdoLCBidXQgdGhpcyB3b3VsZCBvbmx5IGJlIGEgZmV3IHNlbnRlbmNlcyBpbiB0aGUgbWFudXNjcmlwdCBhbmQgd291bGQgYmUgdmFsdWFibGUgSSB0aGluay4KClRvLURvOiBBZGQgTUFOTk9WQSBvciBQRVJNQU5PVkEgdG8gdGhpcwoKIyBTZXR1cAojIyBMb2FkIFBhY2thZ2VzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgUmVxdWlyZWQgUGFja2FnZXMKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyb3BscykKbGlicmFyeShjaGVtaGVscGVyKQojY2hlbWhlbHBlciBjb250YWlucyB0aGUgc2ltX211bHR2YXIoKSBmdW5jdGlvbiBJIHdyb3RlIGFzIHdlbGwgYXMgY3VzdG9tIGZ1bmN0aW9ucyBmb3IgaW50ZXJmYWNpbmcgd2l0aCByb3BscyBwYWNrYWdlIGluIGEgZnJpZW5kbGllciB3YXkuICAKI0luc3RhbGwgd2l0aCBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIkFhcmlxL2NoZW1oZWxwZXIiKQpsaWJyYXJ5KGNvd3Bsb3QpICNmb3IgbWFraW5nIGFuZCBzYXZpbmcgcHJldHRpZXIgcGxvdHMKbGlicmFyeShicm9vbSkgI2ZvciB0aWR5KCksIHdoaWNoIHR1cm5zIG1vZGVsIG91dHB1dCBpbnRvIGRhdGFmcmFtZXMKbGlicmFyeSh2ZWdhbikgI2ZvciBQRVJNQU5PVkEKbGlicmFyeShpaGVhdG1hcHIpICNmb3IgY29ycmVsYXRpb24gaGVhdG1hcHMKYGBgCgojIyBDcmVhdGUgQ3VzdG9tIFBsb3R0aW5nIEZ1bmN0aW9ucwpgYGB7cn0KIyBGdW5jdGlvbnMgZm9yIHBsb3R0aW5nCmxpYnJhcnkobGF0ZXgyZXhwKQpwY2FfcGxvdCA8LSBmdW5jdGlvbihwY2Eub3BscywgZ3JvdXBfdmFyKXsKICBwbG90ZGF0YSA8LSBjaGVtaGVscGVyOjpnZXRfcGxvdGRhdGEocGNhLm9wbHMpCiAgZ2dwbG90KHBsb3RkYXRhJHBsb3RfZGF0YSwgYWVzKHggPSBwMSwgeSA9IHAyLCBjb2xvciA9IGdyb3VwX3ZhcikpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzdGF0X2VsbGlwc2UoKSArCiAgICBsYWJzKHggPSBwYXN0ZTAoIlBDMSAoIiwgcGxvdGRhdGEkdmFyX2V4cGxhaW5lZCRSMlhbMV0gKiAxMDAsICIlKSIpLAogICAgICAgICB5ID0gcGFzdGUwKCJQQzIgKCIsIHBsb3RkYXRhJHZhcl9leHBsYWluZWQkUjJYWzJdICogMTAwLCAiJSkiKSkgKwogICAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKCJHcm91cCBNZW1iZXJzaGlwIikgKwogICAgdGhlbWVfYncoKSArCiAgICBnZ3RpdGxlKCJQQ0EiKSArCiAgICBsYWJzKGNhcHRpb24gPSBUZVgoCiAgICAgIHBhc3RlMChucm93KHBsb3RkYXRhJHZhcl9leHBsYWluZWQpLCAiIHByaW5jaXBhbCBjb21wb25lbnRzOyIsCiAgICAgICAgICAgICAiJFJeMihjdW11bGF0aXZlKSA9ICIsIG1heChwbG90ZGF0YSR2YXJfZXhwbGFpbmVkJGBSMlgoY3VtKWAsICIkIikpKSkKfQoKcGxzZGFfcGxvdCA8LSBmdW5jdGlvbihwbHNkYS5vcGxzKXsKICBwbG90ZGF0YSA8LSBjaGVtaGVscGVyOjpnZXRfcGxvdGRhdGEocGxzZGEub3BscykKICBnZ3Bsb3QocGxvdGRhdGEkcGxvdF9kYXRhLCBhZXMoeCA9IHAxLCB5ID0gcDIsIGNvbG9yID0geTEpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgc3RhdF9lbGxpcHNlKCkgKwogICAgbGFicyh4ID0gcGFzdGUwKCJQMSAoIiwgcGxvdGRhdGEkYXhpc19zdGF0cyRSMlhbMV0gKiAxMDAsICIlKSIpLAogICAgICAgICB5ID0gcGFzdGUwKCJQMiAoIiwgcGxvdGRhdGEkYXhpc19zdGF0cyRSMlhbMl0gKiAxMDAsICIlKSIpKSArCiAgICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiR3JvdXAgTWVtYmVyc2hpcCIpICsKICAgIHRoZW1lX2J3KCkgKwogICAgZ2d0aXRsZSgiUExTLURBIikgKwogICAgbGFicyhjYXB0aW9uID0gVGVYKAogICAgICBwYXN0ZTAoIiRSXnsyfV97WX0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUjJZKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkUV57Mn0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUTIoY3VtKWAsICIkOyAiLAogICAgICAgICAgICAgIiRwX3tRXnsyfX0gPSAiLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRwUTIsICIkIikpKQp9CgpvcGxzZGFfcGxvdCA8LSBmdW5jdGlvbihvcGxzZGEub3Bscyl7CiAgcGxvdGRhdGEgPC0gY2hlbWhlbHBlcjo6Z2V0X3Bsb3RkYXRhKG9wbHNkYS5vcGxzKQogIGdncGxvdChwbG90ZGF0YSRwbG90X2RhdGEsIGFlcyh4ID0gcDEsIHkgPSBvMSwgY29sb3IgPSB5MSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzdGF0X2VsbGlwc2UoKSArCiAgICBsYWJzKHggPSBwYXN0ZTAoIlByZWQgKCIsIHBsb3RkYXRhJGF4aXNfc3RhdHMkUjJYWzFdICogMTAwLCAiJSkiKSwKICAgICAgICAgeSA9IHBhc3RlMCgiT3J0aG8gKCIsIHBsb3RkYXRhJGF4aXNfc3RhdHMkUjJYWzJdICogMTAwLCAiJSkiKSkgKwogICAgc2NhbGVfY29sb3JfZGlzY3JldGUoIkdyb3VwIE1lbWJlcnNoaXAiKSArCiAgICB0aGVtZV9idygpICsKICAgIGdndGl0bGUoIk9QTFMtREEiKSArCiAgICBsYWJzKGNhcHRpb24gPSBUZVgoCiAgICAgIHBhc3RlMCgiJFJeezJ9X3tZfT0iLCBwbG90ZGF0YSRtb2RlbF9zdGF0cyRgUjJZKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkUV57Mn09IiwgcGxvdGRhdGEkbW9kZWxfc3RhdHMkYFEyKGN1bSlgLCAiJDsgIiwKICAgICAgICAgICAgICIkcF97UV57Mn19PSIsIHBsb3RkYXRhJG1vZGVsX3N0YXRzJHBRMiwgIiQiKSkpCn0KYGBgCgojIyBDcmVhdGUgQ3VzdG9tIFN1bW1hcnkgVGFibGUgRnVuY3Rpb24KYGBge3J9Cm15X3RhYmxlIDwtIGZ1bmN0aW9uKGRhdGEsIHBjYSwgcGxzZGEpewogIFZJUHMgPC0gZ2V0X1ZJUChwbHNkYSkKICAKICBsb2FkaW5nc19wbHMgPC0gZ2V0TG9hZGluZ01OKHBsc2RhKSAlPiUKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiVmFyaWFibGUiKSAlPiUgCiAgICByZW5hbWUocDFfbG9hZGluZyA9IHAxLCBwMl9sb2FkaW5nID0gcDIpCiAgCiAgbG9hZGluZ3NfcGNhIDwtIGdldExvYWRpbmdNTihwY2EpICU+JQogICAgYXMuZGF0YS5mcmFtZSgpICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiVmFyaWFibGUiKSAlPiUgCiAgICBzZWxlY3QoVmFyaWFibGUsIFBDMV9sb2FkaW5nID0gcDEsIFBDMl9sb2FkaW5nID0gcDIpCiAgCiAgdF90ZXN0cyA8LSBkYXRhICU+JQogICAgc2VsZWN0KC1ncm91cCkgJT4lCiAgICBtYXBfZGYofnQudGVzdCgufmRhdGEkZ3JvdXApJHAudmFsdWUpICU+JQogICAgZ2F0aGVyKGtleSA9IFZhcmlhYmxlLCB2YWx1ZSA9IHRfdGVzdF9wLnZhbHVlKQogIAogIGpvaW4xIDwtIGZ1bGxfam9pbihWSVBzLCBsb2FkaW5nc19wbHMpCiAgam9pbjIgPC0gZnVsbF9qb2luKGpvaW4xLCBsb2FkaW5nc19wY2EpCiAgam9pbjMgPC0gZnVsbF9qb2luKGpvaW4yLCB0X3Rlc3RzKQogIHJldHVybihqb2luMykKfQpgYGAKCiMgQ3JlYXRlIHNhZmUgdmVyc2lvbiBvZiBvcGxzKCkKT2NjYXNpb25hbGx5IEkgd2FzIGdldHRpbmcgJ21hdHJpeCBpcyBzaW5ndWxhcicgdHlwZSBlcnJvcnMgZnJvbSBgb3BscygpYCwgd2hpY2ggSSB0aGluayBpcyBqdXN0IGEgY2hhbmNlIG9jY3VycmFuY2Ugd2l0aCB0aGUgcmFuZG9tIGRhdGEuICBUaGlzICJzYWZlIiB2ZXJzaW9uIHdpbGwgYWx3YXlzIHJldHVybiBgTlVMTGAgd2hlbiB0aGVyZSBpcyBhbnkga2luZCBvZiBlcnJvci4KYGBge3J9CnNhZmUub3BscyA8LSBwb3NzaWJseShvcGxzLCBvdGhlcndpc2UgPSBOVUxMKQpgYGAKCgojIFNldCBnbG9iYWwgb3B0aW9ucyAobmVlZGxlIGluIGhheXN0YWNrIHNjZW5hcmlvKQpJJ2xsIHNldCBhIGZldyBnbG9iYWwgb3B0aW9ucyBoZXJlIHRvIG1ha2UgaXQgZWFzaWVyIHRvIHBsYXkgYXJvdW5kIHdpdGggdGhlIHNpbXVsYXRpb24uCgpUaGVzZSBvcHRpb25zIGJhc2ljYWxseSBjcmVhdGUgYSBzaXR1YXRpb24gd2hlcmUgd2hlbiB0aGVyZSBhcmUgcmVhbCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwcywgaXQncyBvbmx5IGR1ZSB0byBhIHNtYWxsIHBlcmNlbnRhZ2Ugb2YgdmFyaWFibGVzLiBPdGhlciB2YXJpYWJsZXMgY2FuIGhhdmUgbWlsZCBjb3ZhcmlhdGlvbiBvciBub25lIGF0IGFsbC4KYGBge3IgZ2xvYmFsIG9wdGlvbnN9Ck4gPSAzMCAjdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwpQID0gNDAgI3RvdGFsIG51bWJlciBvZiB2YXJpYWJsZXMKc2lnbmFsLnByb3AgPSAwLjYyNSAjd2hlbiB0aGVyZSBhcmUgY29ycmVsYXRlZCB2YXJpYWJsZXMsIHdoYXQgcHJvcG9ydGlvbj8KY292X2NvcnZhciA9IDAuOCAjY292YXJpYW5jZSBmb3Igc2lnbmFsIHZhcmlhYmxlcwpkaXNjLnByb3AgPSAwLjEyNSAjd2hlbiB0aGVyZSBhcmUgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzLCB3aGF0IHByb3BvcnRpb24/CmRpZmZfZGlzY3IgPSAxLjIgI21lYW4gZGlmZmVyZW5jZSBiZXR3ZWVuIGdyb3VwcyBmb3IgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzCm5wZXJtID0gNTAgI2hvdyBtYW55IHBlcnVtdXRhdGlvbnMKc2VlZCA9IDEwMCAjc2VlZAoKI2NhbGN1bGF0ZWQgdmFsdWVzCihwX2NvcnZhciA9IHJvdW5kKFAqc2lnbmFsLnByb3ApKQoocF9kaXNjciA9IHJvdW5kKFAqZGlzYy5wcm9wKSkKYGBgCgoKIyAxLiBObyBDb3ZhcmlhbmNlLCBObyBEaXNjcmltaW5hdGlvbgojIyBHZW5lcmF0ZSBkYXRhCmBgYHtyfQpzaW0uZGF0YTEgPC0gc2ltX211bHR2YXIocF91bmNvcnZhciA9IFAsCiAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2NyID0gMCwKICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSAwLAogICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBzZWVkKQpzaW0uZGF0YTEgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2ExIDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhMSwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGExIDwtIHRyeShvcGxzKHNlbGVjdChzaW0uZGF0YTEsIC1ncm91cCksIHNpbS5kYXRhMSRncm91cCwKICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkpCmBgYApUaGlzIGZhaWxzLCBidXQgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmcuCmBgYHtyfQojIyBNdXN0IGZvcmNlIGF4ZXMKc2ltLnBsc2RhMSA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTEsIC1ncm91cCksIHNpbS5kYXRhMSRncm91cCwKICAgICAgICAgICAgICAgICAgIHByZWRJID0gMiwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIFBsb3RzOgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwMSA8LSBwbG90X2dyaWQocGNhX3Bsb3Qoc2ltLnBjYTEsIHNpbS5kYXRhMSRncm91cCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIlBDQSIsIHN1YnRpdGxlID0gIk5vIGNvdmFyaWFuY2UsIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTEpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJObyBjb3ZhcmlhbmNlLCBubyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMiKSkKcDEKYGBgCgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KTm90ZTogSXQncyBwcm9iYWJseSBub3QgdmVyeSByZXNwb25zaWJsZSB0byBsb29rIGF0IFZJUCBzY29yZXMgZnJvbSBhIFBMUy1EQSBtb2RlbCB0aGF0IGlzIG5vdCBzaWduaWZpY2FudApgYGB7cn0KbXlfdGFibGUoc2ltLmRhdGExLCBzaW0ucGNhMSwgc2ltLnBsc2RhMSkgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKCiMjIFBlcm11dGF0aW9uIHRlc3RpbmcKRmlyc3QsIG1ha2UgYSBidW5jaCBvZiBkYXRhc2V0cyB3aXRoIHRoZSBzYW1lIHBhcmFtZXRlcnMKYGBge3J9CnNpbS5kYXRhMS5saXN0IDwtIG1hcCgxOm5wZXJtLAogICAgfnNpbV9tdWx0dmFyKHBfdW5jb3J2YXIgPSBQLAogICAgICAgICAgICAgIHBfY29ydmFyID0gMCwKICAgICAgICAgICAgICBwX2Rpc2NyID0gMCwKICAgICAgICAgICAgICBjb3ZfY29ydmFyID0gMCwKICAgICAgICAgICAgICBkaWZmX2Rpc2NyID0gMCwKICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICBzZWVkID0gTkEpKQpuYW1lcyhzaW0uZGF0YTEubGlzdCkgPC0gMTpucGVybQpgYGAKCk5vdywgZG8gUENBIG9uIGFsbCBvZiB0aGVtIGFuZCBnZXQgc2NvcmVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnBjYS5kYXRhMS5saXN0IDwtIG1hcChzaW0uZGF0YTEubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIHBsb3RMID0gRkFMU0UpKSAlPiUgY29tcGFjdCgpCgpzY29yZXMuZGF0YTEubGlzdCA8LSBtYXAocGNhLmRhdGExLmxpc3QsIH5nZXRfcGxvdGRhdGEoLikgJT4lIC4kcGxvdF9kYXRhKQpgYGAKCk5vdyBkbyB0LXRlc3RzIG9uIGFsbCBvZiB0aGVtIGFsb25nIFBDcyAxIGFuZCAyIGFuZCBnZXQgcC12YWx1ZXMKYGBge3J9CiNUaGUgJ2dyb3VwJyB2YXJpYWJsZSBpcyB0aGUgc2FtZSBpbiBhbGwgZGF0YXNldHMuCmdyb3VwaW5nIDwtIHNpbS5kYXRhMS5saXN0W1sxXV0kZ3JvdXAKCnAxLnRlc3RyZXN1bHRzIDwtIHNjb3Jlcy5kYXRhMS5saXN0ICU+JQogICNtYXBzIHQudGVzdCgpIGZ1bmN0aW9uIHRvIGFsbCBkYXRhZnJhbWVzIGFuZCBjb252ZXJ0cyBvdXRwdXQgaW50byBkYXRhZnJhbWUgd2l0aCB0aWR5KCkKICBtYXBfZGZyKH50LnRlc3QoLiRwMSB+IGdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JSAKICAjc2VsZWN0cyBqdXN0IGNvbHVtbnMgb2YgaW50ZXJlc3QKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMxLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMxLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMxLnAudmFsdWUgPSAicC52YWx1ZSIpCgpwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTEubGlzdCAlPiUKICBtYXBfZGZyKH50LnRlc3QoLiRwMiB+IGdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JQogIHNlbGVjdChkYXRhc2V0LAogICAgICAgICBQQzIuZWZmZWN0LnNpemUgPSAiZXN0aW1hdGUiLAogICAgICAgICBQQzIudCA9ICJzdGF0aXN0aWMiLAogICAgICAgICBQQzIucC52YWx1ZSA9ICJwLnZhbHVlIikKCmRhdGExLlBDQXJlc3VsdHMgPC0gYmluZF9jb2xzKHAxLnRlc3RyZXN1bHRzLCBwMi50ZXN0cmVzdWx0cykKYGBgCgpBbmQgZG8gUEVSTUFOT1ZBIG9uIGFsbCBvZiB0aGVtCmBgYHtyfQpkYXRhMS5wZXJtYW5vdmEgPC0gbWFwX2RibChzaW0uZGF0YTEubGlzdCwKICAgIH4gYWRvbmlzKHNlbGVjdCguLCAtZ3JvdXApIH4gZ3JvdXBpbmcsIG1ldGhvZCA9ICJldSIpJGFvdi50YWIkYFByKD5GKWBbMV0pCmBgYAoKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMS5saXN0IDwtIG1hcChzaW0uZGF0YTEubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkKYGBgCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdGExLlBMU3Jlc3VsdHMgPC0gcGxzLmRhdGExLmxpc3QgJT4lCiAgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCgoKZGF0YTEuY29tcGFyaXNvbiA8LSBmdWxsX2pvaW4oZGF0YTEuUENBcmVzdWx0cywgZGF0YTEuUExTcmVzdWx0cykgJT4lIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhMS5wZXJtYW5vdmEpCgpwMS5wZXJtIDwtIGdncGxvdChkYXRhMS5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCIxLiAtQ292YXJpYW5jZSwgLURpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpCnAxLnBlcm0KYGBgCgoKIyAyLiBZZXMgQ292YXJpYW5jZSwgTm8gRGlzY3JpbWluYXRpb24KIyMgR2VuZXJhdGUgRGF0YToKYGBge3J9CnNpbS5kYXRhMiA8LSBzaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgcF9jb3J2YXIgPSBwX2NvcnZhciwKICAgICAgICAgICAgICAgICAgICAgIHBfZGlzY3IgPSAwLAogICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IGNvdl9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICBkaWZmX2Rpc2NyID0gMCwKICAgICAgICAgICAgICAgICAgICAgIE4gPSBOLAogICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQpCgpzaW0uZGF0YTIgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2EyIDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhMiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGEyIDwtIHRyeShvcGxzKHNlbGVjdChzaW0uZGF0YTIsIC1ncm91cCksIHNpbS5kYXRhMiRncm91cCwKICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkpCmBgYApUaGlzIGZhaWxzLCBidXQgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmcuCmBgYHtyfQojIyBNdXN0IGZvcmNlIGF4ZXMKc2ltLnBsc2RhMiA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTIsIC1ncm91cCksIHNpbS5kYXRhMiRncm91cCwKICAgICAgICAgICAgICAgICAgIHByZWRJID0gMiwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKQpgYGAKIyMjIFBsb3RzOgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwMiA8LSBwbG90X2dyaWQocGNhX3Bsb3Qoc2ltLnBjYTIsIHNpbS5kYXRhMiRncm91cCkgKwogICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgICAgICAgICAgICAgIGdndGl0bGUoIlBDQSIsIHN1YnRpdGxlID0gIlllcyBjb3ZhcmlhbmNlLCBubyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMiKSwKICAgICAgICAgICAgICAgcGxzZGFfcGxvdChzaW0ucGxzZGEyKSArCiAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiUExTLURBIiwgc3VidGl0bGUgPSAiWWVzIGNvdmFyaWFuY2UsIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpKQpwMgpgYGAKUEMxIHNsaWdodGx5IGJldHRlciwgYnV0IG92ZXJhbGwgUl4yIHRoZSBzYW1lLiBQTFMtREEgc3RpbGwgbm90IHNpZ25pZmljYW50CgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KTm90ZTogSXQncyBwcm9iYWJseSBub3QgcmVzcG9uc2libGUgdG8gbG9vayBhdCBWSVAgc2NvcmVzIGZyb20gYSBQTFMtREEgbW9kZWwgdGhhdCBpcyBub3Qgc2lnbmlmaWNhbnQKYGBge3J9Cm15X3RhYmxlKHNpbS5kYXRhMiwgc2ltLnBjYTIsIHNpbS5wbHNkYTIpICU+JSBhcnJhbmdlKGRlc2MoVklQKSkKYGBgCgojIyBQZXJtdXRhdGlvbiB0ZXN0aW5nCkZpcnN0LCBtYWtlIGEgYnVuY2ggb2YgZGF0YXNldHMgd2l0aCB0aGUgc2FtZSBwYXJhbWV0ZXJzCmBgYHtyfQpzaW0uZGF0YTIubGlzdCA8LSBtYXAoMTpucGVybSwKICAgICAgICAgICAgICAgICAgICAgIH5zaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfY29ydmFyID0gcF9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IGNvdl9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IE5BKSkKbmFtZXMoc2ltLmRhdGEyLmxpc3QpIDwtIDE6bnBlcm0KYGBgCgpOb3csIGRvIFBDQSBvbiBhbGwgb2YgdGhlbSBhbmQgZ2V0IGxvYWRpbmdzLgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpwY2EuZGF0YTIubGlzdCA8LSBtYXAoc2ltLmRhdGEyLmxpc3QsCiAgICAgICAgICAgICAgICAgICAgICB+c2FmZS5vcGxzKHNlbGVjdCguLCAtZ3JvdXApLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQoKc2NvcmVzLmRhdGEyLmxpc3QgPC0gbWFwKHBjYS5kYXRhMi5saXN0LCB+Z2V0X3Bsb3RkYXRhKC4pICU+JSAuJHBsb3RfZGF0YSkKYGBgCgpOb3cgZG8gdC10ZXN0cyBvbiBhbGwgb2YgdGhlbSBhbG9uZyBQQ3MgMSBhbmQgMiBhbmQgZ2V0IHAtdmFsdWVzCmBgYHtyfQojVGhlICdncm91cCcgdmFyaWFibGUgaXMgdGhlIHNhbWUgaW4gYWxsIGRhdGFzZXRzLgpncm91cGluZyA8LSBzaW0uZGF0YTIubGlzdFtbMV1dJGdyb3VwCgpwMS50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTIubGlzdCAlPiUKICAjbWFwcyB0LnRlc3QoKSBmdW5jdGlvbiB0byBhbGwgZGF0YWZyYW1lcyBhbmQgY29udmVydHMgb3V0cHV0IGludG8gZGF0YWZyYW1lIHdpdGggdGlkeSgpCiAgbWFwX2Rmcih+dC50ZXN0KC4kcDF+Z3JvdXBpbmcpICU+JSB0aWR5KCksIC5pZCA9ICJkYXRhc2V0IikgJT4lIAogICNzZWxlY3RzIGp1c3QgY29sdW1ucyBvZiBpbnRlcmVzdAogIHNlbGVjdChkYXRhc2V0LAogICAgICAgICBQQzEuZWZmZWN0LnNpemUgPSAiZXN0aW1hdGUiLAogICAgICAgICBQQzEudCA9ICJzdGF0aXN0aWMiLAogICAgICAgICBQQzEucC52YWx1ZSA9ICJwLnZhbHVlIikKCiMgcDIudGVzdHJlc3VsdHMgPC0gc2NvcmVzLmRhdGEyLmxpc3QgJT4lCiMgICBtYXBfZGZyKH50LnRlc3QoLiRwMn5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUKIyAgIHNlbGVjdChkYXRhc2V0LAojICAgICAgICAgIFBDMi5lZmZlY3Quc2l6ZSA9ICJlc3RpbWF0ZSIsCiMgICAgICAgICAgUEMyLnQgPSAic3RhdGlzdGljIiwKIyAgICAgICAgICBQQzIucC52YWx1ZSA9ICJwLnZhbHVlIikKCiMgZGF0YTIuUENBcmVzdWx0cyA8LSBiaW5kX2NvbHMocDEudGVzdHJlc3VsdHMsIHAyLnRlc3RyZXN1bHRzKQpkYXRhMi5QQ0FyZXN1bHRzIDwtIHAxLnRlc3RyZXN1bHRzCmBgYAoKQW5kIGRvIFBFUk1BTk9WQSBvbiBhbGwgb2YgdGhlbQpgYGB7cn0KZGF0YTIucGVybWFub3ZhIDwtIG1hcF9kYmwoc2ltLmRhdGEyLmxpc3QsCiAgICB+IGFkb25pcyhzZWxlY3QoLiwgLWdyb3VwKSB+IGdyb3VwaW5nLCBtZXRob2QgPSAiZXUiKSRhb3YudGFiJGBQcig+RilgWzFdKQpgYGAKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMi5saXN0IDwtIG1hcChzaW0uZGF0YTIubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQpgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YTIuUExTcmVzdWx0cyA8LSBwbHMuZGF0YTIubGlzdCAlPiUgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCmRhdGEyLlBMU3Jlc3VsdHMKCmRhdGEyLmNvbXBhcmlzb24gPC0gZnVsbF9qb2luKGRhdGEyLlBDQXJlc3VsdHMsIGRhdGEyLlBMU3Jlc3VsdHMpICU+JQogIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhMi5wZXJtYW5vdmEpCgpwMi5wZXJtIDwtIGdncGxvdChkYXRhMi5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCIyLiArIENvdmFyaWFuY2UsIC0gRGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikKcDIucGVybQpgYGAKCiMgMy4gTm8gQ292YXJpYW5jZSwgWWVzIERpc2NyaW1pbmF0aW9uCiMjIEdlbmVyYXRlIERhdGE6CmBgYHtyfQpzaW0uZGF0YTMgPC0gc2ltX211bHR2YXIocF91bmNvcnZhciA9IFAgLSBwX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9jb3J2YXIgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IHBfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3ZfY29ydmFyID0gY292X2NvcnZhciwKICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzY3IgPSBkaWZmX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgY292X2Rpc2NyID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE4sCiAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gc2VlZCkKc2ltLmRhdGEzICU+JSBzZWxlY3QoLWdyb3VwKSAlPiUgYXMubWF0cml4KCkgJT4lIGNvcigpICU+JSBpaGVhdG1hcChyb3dfbGFiZWxzID0gVFJVRSwgY29sX2xhYmVscyA9IFRSVUUpCmBgYAoKIyMgUENBICYgUExTLURBCiMjIyBSdW4gUENBCmBgYHtyfQpzaW0ucGNhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHBsb3RMID0gRkFMU0UpCmBgYAojIyMgUnVuIFBMUy1EQQpgYGB7cn0Kc2ltLnBsc2RhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHNpbS5kYXRhMyRncm91cCwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgClNpbmdsZSBjb21wb25lbnQgbW9kZWwgb25seSwgc28gZm9yY2UgdHdvIGF4ZXMgZm9yIHRoZSBzYWtlIG9mIHBsb3R0aW5nOgpgYGB7cn0Kc2ltLnBsc2RhMyA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTMsIC1ncm91cCksIHNpbS5kYXRhMyRncm91cCwKICAgICAgICAgICAgICAgICAgIHBlcm1JID0gMjAwLAogICAgICAgICAgICAgICAgICAgcHJlZEkgPSAyLAogICAgICAgICAgICAgICAgICAgcGxvdEwgPSBGQUxTRSkKYGBgCgojIyMgUGxvdHM6CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnAzIDwtIHBsb3RfZ3JpZChwY2FfcGxvdChzaW0ucGNhMywgc2ltLmRhdGEzJGdyb3VwKSArCiAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICAgICAgICAgICAgICAgZ2d0aXRsZSgiUENBIiwgc3VidGl0bGUgPSAiTm8gY292YXJpYW5jZSwgeWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTMpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJObyBjb3ZhcmlhbmNlLCB5ZXMgZGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikpCnAzCmBgYApQTFMtREEgaXMgc3RpbGwgbm90IGdyZWF0LiAgU2luZ2xlIGNvbXBvbmVudCBtb2RlbCBpcyBzaWduaWZjYW50LiAgRm9yY2VkIDIgYXhlcyBmb3IgUExTLURBIHBsb3QuICBJdCBtaWdodCBiZSB0aGUgY2FzZSB0aGF0IHNvbWUgY28tdmFyaWFuY2UgYmV0d2VlbiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMgaXMgcmVxdWlyZWQgZm9yIFBMUy1EQSB0byBwdWxsIHRoZW0gb3V0LiAgSWYgdGhleSBhcmUgY29tcGxldGVseSBvcnRob2dvbmFsLCBob3cgY2FuIGl0IGRyYXcgdGhlICJyZWdyZXNzaW9uIGxpbmUiIHRocm91Z2ggNS1kaW1lbnNpb25hbCBzcGFjZT8KCiMjIEdldCBQQyBheGlzIGxvYWRpbmdzIGFuZCBWSVAgc2NvcmVzLgpgYGB7cn0KbXlfdGFibGUoc2ltLmRhdGEzLCBzaW0ucGNhMywgc2ltLnBsc2RhMykgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKCiMjIFBlcm11dGF0aW9uIHRlc3RpbmcKRmlyc3QsIG1ha2UgYSBidW5jaCBvZiBkYXRhc2V0cyB3aXRoIHRoZSBzYW1lIHBhcmFtZXRlcnMKYGBge3J9CnNpbS5kYXRhMy5saXN0IDwtIG1hcCgxOm5wZXJtLAogICAgICAgICAgICAgICAgICAgICAgfnNpbV9tdWx0dmFyKHBfdW5jb3J2YXIgPSBQIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcF9kaXNjciA9IHBfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2NvcnZhciA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IGRpZmZfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292X2Rpc2NyID0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE4gPSBOLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBOQSkpCmBgYAoKTm93LCBkbyBQQ0Egb24gYWxsIG9mIHRoZW0gYW5kIGdldCBsb2FkaW5ncy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcGNhLmRhdGEzLmxpc3QgPC0gbWFwKHNpbS5kYXRhMy5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkpICU+JQogIGNvbXBhY3QoKSAlPiUKICBzZXRfbmFtZXMoMTpucGVybSkKCnNjb3Jlcy5kYXRhMy5saXN0IDwtIG1hcChwY2EuZGF0YTMubGlzdCwgfmdldF9wbG90ZGF0YSguKSAlPiUgLiRwbG90X2RhdGEpCmBgYAoKTm93IGRvIHQtdGVzdHMgb24gYWxsIG9mIHRoZW0gYWxvbmcgUENzIDEgYW5kIDIgYW5kIGdldCBwLXZhbHVlcwpgYGB7cn0KI1RoZSAnZ3JvdXAnIHZhcmlhYmxlIGlzIHRoZSBzYW1lIGluIGFsbCBkYXRhc2V0cy4KZ3JvdXBpbmcgPC0gc2ltLmRhdGEzLmxpc3RbWzFdXSRncm91cAoKcDEudGVzdHJlc3VsdHMgPC0gc2NvcmVzLmRhdGEzLmxpc3QgJT4lCiAgI21hcHMgdC50ZXN0KCkgZnVuY3Rpb24gdG8gYWxsIGRhdGFmcmFtZXMgYW5kIGNvbnZlcnRzIG91dHB1dCBpbnRvIGRhdGFmcmFtZSB3aXRoIHRpZHkoKQogIG1hcF9kZnIofnQudGVzdCguJHAxfmdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JSAKICAjc2VsZWN0cyBqdXN0IGNvbHVtbnMgb2YgaW50ZXJlc3QKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMxLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMxLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMxLnAudmFsdWUgPSAicC52YWx1ZSIpCgpwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTMubGlzdCAlPiUKICBtYXBfZGZyKH50LnRlc3QoLiRwMn5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUKICBzZWxlY3QoZGF0YXNldCwKICAgICAgICAgUEMyLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKICAgICAgICAgUEMyLnQgPSAic3RhdGlzdGljIiwKICAgICAgICAgUEMyLnAudmFsdWUgPSAicC52YWx1ZSIpCgpkYXRhMy5QQ0FyZXN1bHRzIDwtIGJpbmRfY29scyhwMS50ZXN0cmVzdWx0cywgcDIudGVzdHJlc3VsdHMpCmBgYAoKQW5kIGRvIFBFUk1BTk9WQSBvbiBhbGwgb2YgdGhlbQpgYGB7cn0KZGF0YTMucGVybWFub3ZhIDwtIG1hcF9kYmwoc2ltLmRhdGEzLmxpc3QsCiAgICB+IGFkb25pcyhzZWxlY3QoLiwgLWdyb3VwKSB+IGdyb3VwaW5nLCBtZXRob2QgPSAiZXUiKSRhb3YudGFiJGBQcig+RilgWzFdKQpgYGAKCk5vdyBkbyBQTFMtREEgb24gYWxsIG9mIHRoZW0gYW5kIGdldCBwLXZhbHVlcyAodGhpcyB3aWxsIHRha2UgYSBsb25nIHRpbWUpCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnBscy5kYXRhMy5saXN0IDwtIG1hcChzaW0uZGF0YTMubGlzdCwKICAgICAgICAgICAgICAgICAgICAgIH5zYWZlLm9wbHMoc2VsZWN0KC4sIC1ncm91cCksIGdyb3VwaW5nLCBwcmVkSSA9IDIsIHBlcm1JID0gMjAwLCBwbG90TCA9IEZBTFNFKSkgJT4lIGNvbXBhY3QoKQpgYGAKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGF0YTMuUExTcmVzdWx0cyA8LSBwbHMuZGF0YTMubGlzdCAlPiUgbWFwX2Rmcih+Z2V0X3Bsb3RkYXRhKC4pJG1vZGVsX3N0YXRzLCAuaWQgPSAiZGF0YXNldCIpCmRhdGEzLlBMU3Jlc3VsdHMKCmRhdGEzLmNvbXBhcmlzb24gPC0gZnVsbF9qb2luKGRhdGEzLlBDQXJlc3VsdHMsIGRhdGEzLlBMU3Jlc3VsdHMpICU+JSBhZGRfY29sdW1uKCJwZXJtYW5vdmEiID0gZGF0YTMucGVybWFub3ZhKQoKcDMucGVybSA8LSBnZ3Bsb3QoZGF0YTMuY29tcGFyaXNvbikgKwogIGdlb21fZGVuc2l0eShhZXMoUEMxLnAudmFsdWUpLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBRMiksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjMzKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhwZXJtYW5vdmEpLCBmaWxsID0gImdyZWVuIiwgYWxwaGEgPSAwLjMzKSArCiAgbGFicyh4ID0gInAgdmFsdWUiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMC4wNSwgbGluZXR5cGUgPSA1KSArCiAgZ2d0aXRsZSgiMy4gLSBDb3ZhcmlhbmNlLCArIERpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpCnAzLnBlcm0KYGBgCgojIDQuIFllcyBDb3ZhcmlhbmNlLCBZZXMgRGlzY3JpbWluYXRpb24KIyMgR2VuZXJhdGUgRGF0YToKYGBge3J9CnNpbS5kYXRhNCA8LSBzaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgIHBfY29ydmFyID0gcF9jb3J2YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2NyID0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSBjb3ZfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGlmZl9kaXNjciA9IGRpZmZfZGlzY3IsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3ZfZGlzY3IgPSAwLjYsCiAgICAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSBzZWVkKQpzaW0uZGF0YTQgJT4lIHNlbGVjdCgtZ3JvdXApICU+JSBhcy5tYXRyaXgoKSAlPiUgY29yKCkgJT4lIGloZWF0bWFwKHJvd19sYWJlbHMgPSBUUlVFLCBjb2xfbGFiZWxzID0gVFJVRSkKYGBgCgojIyBQQ0EgJiBQTFMtREEKIyMjIFJ1biBQQ0EKYGBge3J9CnNpbS5wY2E0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkKYGBgCiMjIyBSdW4gUExTLURBCmBgYHtyfQpzaW0ucGxzZGE0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgc2ltLmRhdGE0JGdyb3VwLAogICAgICAgICAgICAgICAgICAgcGVybUkgPSAyMDAsCiAgICAgICAgICAgICAgICAgICBwbG90TCA9IEZBTFNFKQpgYGAKVGhpcyBwcm9kdWNlcyBhIDEtY29tcG9uZW50IG1vZGVsLiAgSSdsbCBmb3JjZSB0d28gYXhlcyBmb3IgdGhlIHNha2Ugb2YgcGxvdHRpbmc6CmBgYHtyfQpzaW0ucGxzZGE0IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNCwgLWdyb3VwKSwgc2ltLmRhdGE0JGdyb3VwLAogICAgICAgICAgICAgICAgICAgcGVybUkgPSAyMDAsCiAgICAgICAgICAgICAgICAgICBwcmVkSSA9IDIsCiAgICAgICAgICAgICAgICAgICBwbG90TCA9IEZBTFNFKQpgYGAKCiMjIyBQbG90czoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KcDQgPC0gcGxvdF9ncmlkKHBjYV9wbG90KHNpbS5wY2E0LCBzaW0uZGF0YTQkZ3JvdXApICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQQ0EiLCBzdWJ0aXRsZSA9ICJZZXMgY292YXJpYW5jZSwgWWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpLAogICAgICAgICAgICAgICBwbHNkYV9wbG90KHNpbS5wbHNkYTQpICsKICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgICAgICAgICAgICBnZ3RpdGxlKCJQTFMtREEiLCBzdWJ0aXRsZSA9ICJZZXMgY292YXJpYW5jZSwgWWVzIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcyIpKQpwNApgYGAKCgojIyBHZXQgUEMgYXhpcyBsb2FkaW5ncyBhbmQgVklQIHNjb3Jlcy4KYGBge3J9Cm15X3RhYmxlKHNpbS5kYXRhNCwgc2ltLnBjYTQsIHNpbS5wbHNkYTQpICU+JSBhcnJhbmdlKGRlc2MoVklQKSkKYGBgCgojIyBQZXJtdXRhdGlvbiB0ZXN0aW5nCkZpcnN0LCBtYWtlIGEgYnVuY2ggb2YgZGF0YXNldHMgd2l0aCB0aGUgc2FtZSBwYXJhbWV0ZXJzCmBgYHtyfQpzaW0uZGF0YTQubGlzdCA8LSBtYXAoMTpucGVybSwKICAgICAgICAgICAgICAgICAgICAgIH5zaW1fbXVsdHZhcihwX3VuY29ydmFyID0gUCAtIHBfY29ydmFyIC0gcF9kaXNjciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX2NvcnZhciA9IHBfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBfZGlzY3IgPSBwX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9jb3J2YXIgPSBjb3ZfY29ydmFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzY3IgPSBkaWZmX2Rpc2NyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdl9kaXNjciA9IDAuMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOID0gTiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gTkEpKSAlPiUgCiAgc2V0X25hbWVzKDE6bnBlcm0pCmBgYAoKTm93LCBkbyBQQ0Egb24gYWxsIG9mIHRoZW0gYW5kIGdldCBsb2FkaW5ncy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KcGNhLmRhdGE0Lmxpc3QgPC0gbWFwKHNpbS5kYXRhNC5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgcGxvdEwgPSBGQUxTRSkpICU+JQogIGNvbXBhY3QoKQpzY29yZXMuZGF0YTQubGlzdCA8LSBtYXAocGNhLmRhdGE0Lmxpc3QsIH5nZXRfcGxvdGRhdGEoLikgJT4lIC4kcGxvdF9kYXRhKQpgYGAKCk5vdyBkbyB0LXRlc3RzIG9uIGFsbCBvZiB0aGVtIGFsb25nIFBDcyAxIGFuZCAyIGFuZCBnZXQgcC12YWx1ZXMKYGBge3J9CiNUaGUgJ2dyb3VwJyB2YXJpYWJsZSBpcyB0aGUgc2FtZSBpbiBhbGwgZGF0YXNldHMuCmdyb3VwaW5nIDwtIHNpbS5kYXRhNC5saXN0W1sxXV0kZ3JvdXAKCnAxLnRlc3RyZXN1bHRzIDwtIHNjb3Jlcy5kYXRhNC5saXN0ICU+JQogICNtYXBzIHQudGVzdCgpIGZ1bmN0aW9uIHRvIGFsbCBkYXRhZnJhbWVzIGFuZCBjb252ZXJ0cyBvdXRwdXQgaW50byBkYXRhZnJhbWUgd2l0aCB0aWR5KCkKICBtYXBfZGZyKH50LnRlc3QoLiRwMX5ncm91cGluZykgJT4lIHRpZHkoKSwgLmlkID0gImRhdGFzZXQiKSAlPiUgCiAgI3NlbGVjdHMganVzdCBjb2x1bW5zIG9mIGludGVyZXN0CiAgc2VsZWN0KGRhdGFzZXQsCiAgICAgICAgIFBDMS5lZmZlY3Quc2l6ZSA9ICJlc3RpbWF0ZSIsCiAgICAgICAgIFBDMS50ID0gInN0YXRpc3RpYyIsCiAgICAgICAgIFBDMS5wLnZhbHVlID0gInAudmFsdWUiKQoKIyBwMi50ZXN0cmVzdWx0cyA8LSBzY29yZXMuZGF0YTQubGlzdCAlPiUKIyAgIG1hcF9kZnIofnQudGVzdCguJHAyfmdyb3VwaW5nKSAlPiUgdGlkeSgpLCAuaWQgPSAiZGF0YXNldCIpICU+JQojICAgc2VsZWN0KGRhdGFzZXQsCiMgICAgICAgICAgUEMyLmVmZmVjdC5zaXplID0gImVzdGltYXRlIiwKIyAgICAgICAgICBQQzIudCA9ICJzdGF0aXN0aWMiLAojICAgICAgICAgIFBDMi5wLnZhbHVlID0gInAudmFsdWUiKQoKIyBkYXRhNC5QQ0FyZXN1bHRzIDwtIGJpbmRfY29scyhwMS50ZXN0cmVzdWx0cywgcDIudGVzdHJlc3VsdHMpCmRhdGE0LlBDQXJlc3VsdHMgPC0gcDEudGVzdHJlc3VsdHMKYGBgCgpBbmQgZG8gUEVSTUFOT1ZBIG9uIGFsbCBvZiB0aGVtCmBgYHtyfQpkYXRhNC5wZXJtYW5vdmEgPC0gbWFwX2RibChzaW0uZGF0YTQubGlzdCwKICAgIH4gYWRvbmlzKHNlbGVjdCguLCAtZ3JvdXApIH4gZ3JvdXBpbmcsIG1ldGhvZCA9ICJldSIpJGFvdi50YWIkYFByKD5GKWBbMV0pCmBgYAoKTm93IGRvIFBMUy1EQSBvbiBhbGwgb2YgdGhlbSBhbmQgZ2V0IHAtdmFsdWVzICh0aGlzIHdpbGwgdGFrZSBhIGxvbmcgdGltZSkKYGBge3IgaW5jbHVkZT1GQUxTRX0KcGxzLmRhdGE0Lmxpc3QgPC0gbWFwKHNpbS5kYXRhNC5saXN0LAogICAgICAgICAgICAgICAgICAgICAgfnNhZmUub3BscyhzZWxlY3QoLiwgLWdyb3VwKSwgZ3JvdXBpbmcsIHByZWRJID0gMiwgcGVybUkgPSAyMDAsIHBsb3RMID0gRkFMU0UpKSAlPiUgY29tcGFjdCgpCmBgYApgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhNC5QTFNyZXN1bHRzIDwtIHBscy5kYXRhNC5saXN0ICU+JSBtYXBfZGZyKH5nZXRfcGxvdGRhdGEoLikkbW9kZWxfc3RhdHMsIC5pZCA9ICJkYXRhc2V0IikKZGF0YTQuUExTcmVzdWx0cwoKZGF0YTQuY29tcGFyaXNvbiA8LSBmdWxsX2pvaW4oZGF0YTQuUENBcmVzdWx0cywgZGF0YTQuUExTcmVzdWx0cykgJT4lIGFkZF9jb2x1bW4oInBlcm1hbm92YSIgPSBkYXRhNC5wZXJtYW5vdmEpCgpwNC5wZXJtIDwtIGdncGxvdChkYXRhNC5jb21wYXJpc29uKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhQQzEucC52YWx1ZSksIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4zMykgKwogIGdlb21fZGVuc2l0eShhZXMocFEyKSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMzMpICsKICBnZW9tX2RlbnNpdHkoYWVzKHBlcm1hbm92YSksIGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuMzMpICsKICBsYWJzKHggPSAicCB2YWx1ZSIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjA1LCBsaW5ldHlwZSA9IDUpICsKICBnZ3RpdGxlKCI0LiArIENvdmFyaWFuY2UsICsgRGlzY3JpbWluYXRpbmcgdmFyaWFibGVzIikKcDQucGVybQpgYGAKCiMgUGxvdCBleGFtcGxlIGRhdGFzZXRzCmBgYHtyfQpwbG90cyA8LSBwbG90X2dyaWQocDEscDIscDMscDQsIG5jb2wgPSAxLCBucm93ID0gNCkKc2F2ZV9wbG90KCJmaWdzL2ZpZzEucG5nIiwgcGxvdHMsIG5jb2wgPSAyLCBucm93ID0gNCwgYmFzZV9hc3BlY3RfcmF0aW8gPSAxLCBiYXNlX2hlaWdodCA9IDMuNSkKYGBgCgoKIyBQbG90IHBlcm11dGF0aW9uIHRlc3RpbmcgcmVzdWx0cwpgYGB7cn0KcGxvdHMucGVybSA8LSBwbG90X2dyaWQocDEucGVybSwgcDIucGVybSwgcDMucGVybSwgcDQucGVybSwgbmNvbCA9IDIsIG5yb3cgPSAyKQpwLnZhbF9maWd1cmUgPC0gcGxvdF9ncmlkKGdncGxvdCgpICsgZ2d0aXRsZSgiRGlzdHIuIG9mIHAgdmFsdWVzIGZyb20gdC10ZXN0cyBvbiBQQzEgKGJsdWUpLCBQTFMtREEgKHJlZCksIFBFUk1BTk9WQSAoZ3JlZW4pIiksIHBsb3RzLnBlcm0sIG5jb2wgPSAxLCByZWxfaGVpZ2h0cyA9IGMoMC4xLCAxKSkKcC52YWxfZmlndXJlCnNhdmVfcGxvdCgiZmlncy9maWcyLnBuZyIsIHAudmFsX2ZpZ3VyZSwgbmNvbCA9IDIsIG5yb3cgPSAyLCBiYXNlX2FzcGVjdF9yYXRpbyA9IDEuNSkKYGBgCgojIENvbmNsdXNpb25zCk5vbmUgb2YgdGhlIG1ldGhvZHMgZ2l2ZSBtb3JlIGZhbHNlIHBvc2l0aXZlcyB0aGFuIHRoZXkgc2hvdWxkLS0tdGhhdCBpcywgbm8gZGlmZmVyZW5jZXMgYXJlIGZvdW5kIHdoZW4gdGhlcmUgYXJlIG5vIGRpc2NyaW1pbmF0aW5nIHZhcmlhYmxlcy4KClBFUk1BTk9WQSBzZWVtcyB0byBwZXJmb3JtIHRoZSBiZXN0IGluIHRoZXNlIHNpbXVsYXRpb25zLCBmb2xsb3dlZCBieSBQTFMtREEuICBQQ0EgaXMgc2hpdCBhdCBmaW5kaW5nIHNlcGFyYXRpb24gd2hlbiBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMgYXJlIGEgc21hbGwgcG9ydGlvbiBvZiB0aGUgdG90YWwgdmFyaWFibGVzIG1lYXN1cmVkIChuZWVkbGUgaW4gYSBoYXlzdGFjayBzY2VuYXJpbykuCgpXaGVuIGNvdmFyaWFuY2UgaXMgYWRkZWQgdG8gdGhlICJoYXlzdGFjayIsIG9yIHRvIGJvdGggdGhlICJoYXlzdGFjayIgYW5kIHRoZSAibmVlZGxlIiwgdGhlIHBlcmZvcm1hbmNlIG9mIFBFUk1BTk9WQSBkcm9wcywgd2hpbGUgdGhlIHBlcmZvcm1hbmNlIG9mIFBMUy1EQSByZW1haW5zIGFib3V0IHRoZSBzYW1lLiBJIHNob3VsZCBwbGF5IGFyb3VuZCB3aXRoIHRoaXMgaW4gYSBzZXBhcmF0ZSBub3RlYm9vay4KCgojIFRyeWluZyBhbHRlcm5hdGUgbWV0aG9kIG9mIG1ha2luZyBkaXNjcmltaW5hdGluZyB2YXJpYWJsZXMKCkkgY2FuJ3QgZXZlbiBmaWd1cmUgb3V0IGhvdyB0byBtYWtlIFBMUy1EQSBmaW5kIGEgZGlmZmVyZW5jZS4KYGBge3J9CnNpbS5kYXRhNSA8LSBzaW0udmNvdihwX25vaXNlID0gMCwKICAgICAgICAgICAgICAgICAgICAgIHBfc2lnbmFsID0gMTUsCiAgICAgICAgICAgICAgICAgICAgICBwX2Rpc2MgPSAxNSwKICAgICAgICAgICAgICAgICAgICAgIGNvdl9zaWduYWwgPSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICBjb3ZfZGlzYyA9IDAuNiwKICAgICAgICAgICAgICAgICAgICAgIGRpZmZfZGlzYyA9IDAsCiAgICAgICAgICAgICAgICAgICAgICB2YXIgPSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICBOID0gMjAsCiAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gTkEpCgpzaW0uZGF0YTUgPC0gc2ltLmRhdGE1ICU+JQogIHJlbmFtZShZID0gZGlzY18xKSAlPiUgCiAgYXJyYW5nZShZKSAlPiUgCiAgbXV0YXRlKGdyb3VwID0gYyhyZXAoImEiLCBucm93KC4pLzIpLCByZXAoImIiLCBucm93KC4pLzIpKSkKYGBgCgpgYGB7cn0KcGNhNSA8LSBvcGxzKHNlbGVjdChzaW0uZGF0YTUsIC1ZLCAtZ3JvdXApLCBwcmVkSSA9IDIpCnBjYV9wbG90KHBjYTUsIHNpbS5kYXRhNSRncm91cCkKYGBgCgpgYGB7cn0KcGxzZGE1IDwtIG9wbHMoc2VsZWN0KHNpbS5kYXRhNSwgLWdyb3VwLCAtWSksIHNpbS5kYXRhNSRncm91cCwgcGVybUkgPSAyMDAsIHByZWRJID0gMikKYGBgCmBgYHtyfQpnZXRfVklQKHBsc2RhNSkgJT4lIGFycmFuZ2UoZGVzYyhWSVApKQpgYGAKYGBge3J9CnBsczUgPC0gb3BscyhzZWxlY3Qoc2ltLmRhdGE1LCAtZ3JvdXAsIC1ZKSwgc2ltLmRhdGE1JFksIHBlcm1JID0gMjAwLCBwcmVkSSA9IDIpCmBgYAoKSHVoLCB0aGF0IGRvZXNuJ3Qgd29yawpgYGB7cn0KcGxvdGRhdGEgPC0gc2ltLmRhdGE1ICU+JSAKICBnYXRoZXIoLWdyb3VwLCAtWSwga2V5ID0gdmFyaWFibGUsIHZhbHVlID0gZGF0YSkgJT4lIAogIG11dGF0ZSh2YXJ0eXBlID0gY2FzZV93aGVuKHN0cl9kZXRlY3QodmFyaWFibGUsICJkaXNjIikgfiAiZGlzY3JpbWluYXRpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QodmFyaWFibGUsICJub2lzZSIpIH4gIm5vIGNvdmFyaWFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QodmFyaWFibGUsICJzaWduYWwiKSB+ICJjb3ZhcnlpbmciKSkKZ2dwbG90KHBsb3RkYXRhLCBhZXMoeCA9IFksIHkgPSBkYXRhLCBjb2xvciA9IHZhcnR5cGUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSJsbSIsIHNlID0gRkFMU0UpCmBgYAoqQWggaGEhIElzIGl0IGJlY2F1c2UgdGhlIGRpc2NyaW1pbmF0aW5nIGRhdGEgYWN0dWFsbHkgZG9lcyBOT1QgY28tdmFyeT8qCk5vLCBJIHVwcGVkIHRoZSBjb3ZhcmlhbmNlIGFuZCBpdCBkb2Vzbid0IGhlbHAuCgpQZXJtYW5vdmEKYGBge3J9CnBlcm1kYXRhNSA8LSBzaW0uZGF0YTUgJT4lCiAgIyBzZWxlY3QoLVkpICU+JSAKICBtdXRhdGVfaWYoaXMuZG91YmxlLCBzY2FsZSkKYWRvbmlzKHNlbGVjdChwZXJtZGF0YTUsIC1ncm91cCwgLVkpfnBlcm1kYXRhNSRncm91cCpwZXJtZGF0YTUkWSwgbWV0aG9kID0gImV1IikKYGBgCgo=